using System;
using System.IO;
using System.Xml;
using System.Collections.Generic;

namespace SanFS.DiscLibrary
{
	[Serializable]
	public class Disc : IDisc
	{
		private	EDiscType		mType;
		private bool			mRewritable;
		private EDiscStatus		mDiscStatus;
		private ulong			mTotalSize;
		private ulong			mFreeSpace;
		private int				mMaxWriteSpeed = 1;
		private Guid			mUuid;
		private int				mVersion;
		private List<IDiscFile>	mFiles = new List<IDiscFile>();
		
		public Disc()
		{
		}
		
		public Disc(string XmlConfigFile)
		{
			ReadMetaInfo(XmlConfigFile);
		}
		
		public Disc(EDiscType Type, bool Rewriteable, EDiscStatus DiscStatus)
		{
			mType = Type;
			mRewritable = Rewriteable;
			mDiscStatus = DiscStatus;
		}
		
		public Disc(EDiscType Type, bool Rewriteable, EDiscStatus DiscStatus, ulong TotalSize, 
							ulong FreeSpace, int MaxWriteSpeed, Guid Uuid, int Version,
							List<IDiscFile> Files) {
							
			mType = Type;
			mRewritable = Rewriteable;
			mDiscStatus = DiscStatus;

            if (mDiscStatus == EDiscStatus.Blank)
            {
                if (Uuid != Guid.Empty)
                    throw new ArgumentException("Non Empty Guid on blank disc", "Uuid");
                if (Files != null)
                    throw new ArgumentException("Non null file list on blank disc", "Files");
            }
			
			mTotalSize = TotalSize;
			mFreeSpace = FreeSpace;
			mMaxWriteSpeed = MaxWriteSpeed;
			mUuid = Uuid;
			mVersion = Version;
			mFiles = Files;
		}
		
		public void ReadMetaInfo(string XmlConfigFile)
		{
			XmlDocument	doc	= new XmlDocument();
			
			doc.Load(XmlConfigFile);
			
			XmlNode	discNode = doc.GetElementsByTagName("Disc")[0];
			mUuid = new Guid(discNode.Attributes.GetNamedItem("Uuid").Value);
			mVersion = Convert.ToInt32(discNode.Attributes.GetNamedItem("Version").Value);
			int	numFiles = Convert.ToInt32(discNode.Attributes.GetNamedItem("NumFiles").Value);
			
			foreach (XmlNode node in discNode.ChildNodes)
			{
				if (node.Name.ToLower() == "file")
					mFiles.Add((IDiscFile) new DiscFile(node));
				else
					throw new Exception(String.Format("Unknown XML token '{0}'", node.Name));
			}
			
			if (mFiles.Count != numFiles)
				throw new Exception(String.Format("NumFiles {0} != {1}",mFiles.Count, numFiles));
		}
		
		public void WriteMetaInfo(string XmlConfigFile)
		{
			XmlDocument		doc = new XmlDocument();
			XmlNode			discNode;
			XmlElement		discElement;
			XmlAttribute	attrib;

			discNode = doc.CreateNode(XmlNodeType.XmlDeclaration, "", "");
			doc.AppendChild(discNode);

			discElement = doc.CreateElement("Disc");
			doc.AppendChild(discElement);

			attrib = doc.CreateAttribute("Uuid");
			attrib.Value = this.mUuid.ToString();
			discElement.SetAttributeNode(attrib);

			attrib = doc.CreateAttribute("Version");
			attrib.Value = this.mVersion.ToString();
			discElement.SetAttributeNode(attrib);
			
			attrib = doc.CreateAttribute("NumFiles");
			attrib.Value = mFiles.Count.ToString();
			discElement.SetAttributeNode(attrib);

			foreach (IDiscFile file in mFiles)
			{
				XmlElement	fileElement = doc.CreateElement("File");
				discElement.AppendChild(fileElement);
				
				attrib = doc.CreateAttribute("Uuid");
				attrib.Value = file.Uuid.ToString();
				fileElement.SetAttributeNode(attrib);
				
				attrib = doc.CreateAttribute("TotalSize");
				attrib.Value = file.TotalSize.ToString();
				fileElement.SetAttributeNode(attrib);
				
				attrib = doc.CreateAttribute("TotalChunks");
				attrib.Value = file.TotalChunks.ToString();
				fileElement.SetAttributeNode(attrib);
				
				attrib = doc.CreateAttribute("ChunkNumber");
				attrib.Value = file.ChunkNumber.ToString();
				fileElement.SetAttributeNode(attrib);
				
				attrib = doc.CreateAttribute("ChunkSize");
				attrib.Value = file.ChunkSize.ToString();
				fileElement.SetAttributeNode(attrib);
				
				attrib = doc.CreateAttribute("ChunkOffset");
				attrib.Value = file.ChunkOffset.ToString();
				fileElement.SetAttributeNode(attrib);
				
				attrib = doc.CreateAttribute("Path");
				attrib.Value = file.Path;
				fileElement.SetAttributeNode(attrib);
			}
			doc.Save(XmlConfigFile);
		}
		

		public void RefreshPhysInfo(Drive DiscDrive)
		{
			CmdWrapper	cmd = new CmdWrapper("dvd+rw-mediainfo " + DiscDrive.Dev);
			string		[]output = cmd.Run();
			int			currentTrack = 0;
			 
			foreach (string rawLine in output)
			{
				string	line = rawLine.Trim();
				
				if (line.StartsWith("Mounted Media:"))
				{
					string	tmp = line.Split(',')[1].Trim();
					
					if (tmp == "DVD-R Sequential")
						mType = EDiscType.DVDR_SL;
					else if (tmp == "DVD-RW Sequential")
					{
						mType = EDiscType.DVDR_SL;
						mRewritable = true;
					}
					else if (tmp == "DVD-ROM")
						mType = EDiscType.DVD;
					else
						throw new Exception(String.Format("Unknown media type '{0}'", tmp));
				}
				else if (line.StartsWith("Write Speed"))
				{
					string	tmp = line.Split(':')[1].Trim().Split('.')[0];
					int		wrspeed = Convert.ToInt32(tmp);
					
					if (mMaxWriteSpeed < wrspeed)
						mMaxWriteSpeed = wrspeed;
				}
				else if (line.StartsWith("Disc status"))
				{
					string	status = line.Split(':')[1].Trim();
					
					if (status == "blank")
						mDiscStatus = EDiscStatus.Blank;
					else if (status == "appendable")
						mDiscStatus = EDiscStatus.Appendable;
					else if (status == "complete")
						mDiscStatus = EDiscStatus.Complete;
					else
						throw new Exception(String.Format("Unknown disk status '{0}'", status));
				}
				else if (line.StartsWith("Number of Sessions"))
				{
					//mNumSessions = Convert.ToInt32(line.Split(':')[1].Trim());
				}
				else if (line.StartsWith("State of Last Session:"))
				{
				}
				else if (line.StartsWith("READ TRACK INFORMATION"))
				{
					currentTrack = Convert.ToInt32(line.Split('#')[1].Split(']')[0]);
				}
				else if (line.StartsWith("Free Blocks:") && currentTrack == 1)
				{
					uint	blks = Convert.ToUInt32(line.Split(':')[1].Trim().Split('*')[0]);
					string	blksz = line.Split('*')[1];
					
					if (blksz == "2KB")
						this.mFreeSpace = blks * 2048;
					else
						throw new Exception(String.Format("Unsupported block size '{0}'", blksz));

				}
				else if (line.StartsWith("Track Size:") && currentTrack == 1)
				{
					uint	blks = Convert.ToUInt32(line.Split(':')[1].Trim().Split('*')[0]);
					string	blksz = line.Split('*')[1];
					
					if (blksz == "2KB")
						this.mTotalSize = blks * 2048;
					else
						throw new Exception(String.Format("Unsupported block size '{0}'", blksz));
				}
				
			}
			
			Console.WriteLine("Disc.RefreshPhysInfo(): Type {0}, RW {1}, ST {2}, Size {3}, Free {4}, MW {5}, NS {6}",
								mType,
								mRewritable,
								mDiscStatus,
								mTotalSize,
								mFreeSpace,
								mMaxWriteSpeed, 0);
								//mNumSessions);
		}
		
		/*
		 * Public Properties
		 */ 
		public EDiscType Type
		{
			get { return mType; }
			set { mType = value; }
		}
		
		public bool	Rewritable
		{
			get { return mRewritable; }
			set { mRewritable = value; }
		}
		
		public EDiscStatus	Status
		{
			get { return mDiscStatus;}
			set { mDiscStatus = value; }
		}
		
		public ulong TotalSize
		{
			get { return mTotalSize; }
			set { mTotalSize = value; }
		}
		
		public ulong FreeSpace
		{
			get { return mFreeSpace; }
		}
		
		public int MaxWriteSpeed
		{
			get
			{
				if (this.mDiscStatus == EDiscStatus.Pending)
					throw new Exception("Max write speed is unknown when Disc is pending");
				return mMaxWriteSpeed;
			}
		}
		
		public Guid Uuid
		{
			get { return mUuid; }
			set { mUuid = value; }
		}
		
		public int Version
		{
			get { return mVersion; }
			set { mVersion = value; }
		}

		public List<IDiscFile> Files
		{
			get { return mFiles; }
		}
		
	}  // END CLASS
}  // END NAMESPACE
